/***************************************************************************
                          octopus.c  -  description
                             -------------------
    begin                : Aug  2007
    copyright            : (C) 2007 by empronix
    autho                : Benedikt Sauter, sauter@empronix.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software, you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License           *
 *   version 2.1 as published by the Free Software Foundation.             *
 *                                                                         *
 ***************************************************************************/

/**
    \mainpage liboctopus API documentation

    Library to talk to octopus devices. You find the latest versions of liboctopus at
    http://www.empronix.org/

    The library is easy to use. Have a look at this short example:
    \include simple.c

    More examples can be found in the "examples" directory.
*/
/** \addtogroup liboctopus */
/* @{ */

#include <usb.h>
#include <string.h>
#include <errno.h>

#include "octopus.h"
#include "../../firmware/protocol.h"

#define octopus_error_return(code, str) do {  \
        octopus->error_str = str;             \
	return code;                       \
	} while(0);



int octopus_init(struct octopus_context *octopus)
{
  if(octopus==NULL)
    octopus_error_return(-1,"octopus not valid");

  octopus->error_str = NULL;
  octopus->usb_handle = NULL;


  return 1;
}

int octopus_open(struct octopus_context *octopus)
{
  if(octopus_open_id(octopus,VID,PID)<0)
    octopus_error_return(-1,"could not found octopus device with pid and vid");
  return 1;
}


int octopus_open_id(struct octopus_context *octopus, int vendor, int product)
{
  struct usb_bus *busses;
  struct usb_dev_handle* usb_handle;
  struct usb_bus *bus;
  struct usb_device *dev;

  usb_init();

  if (usb_find_busses() < 0)
    octopus_error_return(-1, "usb_find_busses() failed");
  if (usb_find_devices() < 0)
    octopus_error_return(-2, "usb_find_devices() failed");
  
  busses = usb_get_busses();
  if(busses==NULL)
    octopus_error_return(-3, "usb_get_busses() failed");
  
  for (bus = busses; bus; bus = bus->next){
    for (dev = bus->devices; dev; dev = dev->next){
      if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) {
	octopus_open_dev(octopus,dev);
	return 1;
      }
    }
  }
  octopus_error_return(-4,"could not found octopus device with pid and vid");
  return -1;


}


int octopus_open_serial(struct octopus_context *octopus, char * serial)
{
  struct usb_bus *busses;
  struct usb_dev_handle* usb_handle;
  struct usb_bus *bus;
  struct usb_device *dev;
  char usbserial[64];
  int length=0;

  usb_init();

  if (usb_find_busses() < 0)
    octopus_error_return(-1, "usb_find_busses() failed");
  if (usb_find_devices() < 0)
    octopus_error_return(-2, "usb_find_devices() failed");
  
  busses = usb_get_busses();
  if(busses==NULL)
    octopus_error_return(-3, "usb_get_busses() failed");
  
  for (bus = busses; bus; bus = bus->next){
    for (dev = bus->devices; dev; dev = dev->next){
      if (dev->descriptor.idVendor == VID && dev->descriptor.idProduct == PID) {
	
	usb_handle = usb_open(dev);
	usb_set_configuration(usb_handle,0);
	length = usb_get_string_simple(usb_handle,3,usbserial,64);
	usb_close(usb_handle);
	
	if(strncmp(serial,usbserial,length)==0){
	  octopus_open_dev(octopus,dev);
	  return 1;
	}
      }
    }
  }
  octopus_error_return(-4,"could not found octopus device with serial number");
  return -1;


}

int octopus_open_dev(struct octopus_context *octopus, struct usb_device *dev)
{
  if(octopus==NULL)
    octopus_error_return(-1,"octopus handle is wrong or missing");
  
  if(dev==NULL)
    octopus_error_return(-2,"device handle is wrong or missing");
  
  if(!(octopus->usb_handle = usb_open(dev)))
    octopus_error_return(-3,"can't open usb device");

  if(!usb_set_configuration (octopus->usb_handle,dev->config[0].bConfigurationValue))
    octopus_error_return(-4,"can't set configuration for given usb device");
   
  if(!usb_claim_interface(octopus->usb_handle, 0))
    octopus_error_return(-5,"can't claim interface for given usb device");
  
  if(!usb_set_altinterface(octopus->usb_handle,0))
    octopus_error_return(-6,"can't set altinterface for given usb device");

  return 1;
}


int octopus_close(struct octopus_context *octopus)
{
  if(octopus==NULL)
    octopus_error_return(-1,"octopus is not a valid handle");
  
  if(usb_close(octopus->usb_handle)<0)
    octopus_error_return(-2,usb_strerror());

  return 1;
}

int octopus_message(struct  octopus_context *octopus, unsigned char *msg,
		    int msglen, unsigned char *answer, int answerlen)
{
  if(usb_bulk_write(octopus->usb_handle,1,msg,msglen,100)<msglen)
    octopus_error_return(-1,"transfer error occurred"); 

  if(answerlen>0){
    int timeout=0; 
    int recv;
    while(1){
      if(usb_bulk_read(octopus->usb_handle,0x81,answer,answerlen,100)>0)
	break;
      if(timeout>=1000)
	octopus_error_return(-2,"receive error occurred");
      timeout++;
    }

    if(answer[1]==RSP_OK){
      return 1;
    } else if (answer[1]==RSP_UNKOWN_CMD) {
      octopus_error_return(-1,"unkown command"); 
    } else if (answer[1]==RSP_UNKOWN_PIN) {
      octopus_error_return(-2,"unkown pin"); 
    } else if (answer[1]==RSP_WRONG_PIN_CONFIG) {
      octopus_error_return(-3,"pin is in wrong configuration"); 
    } else if (answer[1]==RSP_ERROR) {
      octopus_error_return(-4,"error in usb device"); 
    } else if (answer[1]==RSP_TIMEOUT) {
      octopus_error_return(-4,"timeout error in usb device"); 
    } else {
      octopus_error_return(-5,"unknown error"); 
    }      
  }
  return 1;
}

int octopus_reset(struct octopus_context *octopus){}

int octopus_status(struct octopus_context *octopus){}
int octopus_logging(struct octopus_context *octopus){}

char * octopus_get_hwdesc(struct octopus_context *octopus, char *desc)
{

  char msg[] = {CMD_GET_HW_ID,0x00,0x00};
  int i;
  char answer[64];
  for(i=0;i<64;i++)
    answer[i]=0;

  if(octopus_message(octopus,msg,3,answer,13)==RSP_OK){
    if((int)answer[2]>0)
      memcpy(desc,answer+3,(int)answer[2]);
      return desc;
  } else {
    octopus_error_return(NULL,octopus->error_str);
  }
  return desc;
}


int octopus_io_init(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_IO_INIT_PIN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}

int octopus_io_init_port(struct octopus_context *octopus, int port)
{
  char msg[] = {CMD_IO_INIT_PORT,0x01,0x00};
  char answer[2];
  msg[2] = (char)port;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}

int octopus_io_set_port_direction_out(struct octopus_context *octopus, int port,unsigned char mask)
{
  char answer[2];
  char msg[] = {CMD_IO_PORT_DIRECTION_OUT,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,2))
    return 1;
  else return -1;
}

int octopus_io_set_port_direction_in(struct octopus_context *octopus, int port, unsigned char mask)
{
  char answer[2];
  char msg[] = {CMD_IO_PORT_DIRECTION_IN,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,2))
    return 1;
  else return -1;
}


int octopus_io_set_port_direction_tri(struct octopus_context *octopus, int port, unsigned char mask)
{
  char answer[2];
  char msg[] = {CMD_IO_PORT_DIRECTION_TRI,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,2))
    return 1;
  else return -1;
}


int octopus_io_set_pin_direction_out(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_IO_PIN_DIRECTION_OUT,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,2)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}


int octopus_io_set_pin_direction_in(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_IO_PIN_DIRECTION_IN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,2)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_io_set_pin_direction_tri(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_IO_PIN_DIRECTION_TRI,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,2)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}

unsigned char octopus_io_get_port(struct octopus_context *octopus, int port)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_GET,0x01,0x00};
  msg[2] = (char)port;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return (unsigned char) answer[2];
  else 
    octopus_error_return((unsigned char)NULL,octopus->error_str);
}

int octopus_io_get_port_bulk(struct octopus_context *octopus, int port, int number, unsigned char * buf)
{

}

int octopus_io_set_port(struct octopus_context *octopus, int port, unsigned char value)
{
  char answer[2];
  char msg[] = {CMD_IO_PORT_SET,0x01,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = (char)value;
  if(octopus_message(octopus,msg,4,answer,2))
    return 1;
  else return -1;
}

int octopus_io_set_port_bulk(struct octopus_context *octopus, int port, int number, unsigned char * buf)
{

}

int octopus_io_set_pin(struct octopus_context *octopus, int pin, int value)
{
  char answer[2];
  char msg[] = {CMD_IO_PIN_SET,0x01,0x00,0x00};
  msg[2] = (char)pin;
  msg[3] = (char)value;
  if(octopus_message(octopus,msg,4,answer,2)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}  

int octopus_io_set_pin_bulk(struct octopus_context *octopus, int pin, int number, int * buf)
{

}

int octopus_io_get_pin(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_IO_PIN_GET,0x01,0x00};
  msg[2] = (char)pin;
  msg[3] = 0x00;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);
}

int octopus_io_get_pin_bulk(struct octopus_context *octopus, int pin, int number, int * buf)
{

}

/// part: adc
int octopus_adc_init(struct octopus_context *octopus, int pin)
{
  char answer[2];
  char msg[] = {CMD_ADC_INIT_PIN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}

int octopus_adc_get(struct octopus_context *octopus, int pin)
{
  unsigned char answer[2];
  unsigned char msg[] = {CMD_ADC_GET,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,4)>0){
    //unsigned char low = answer[3]; 
    //unsigned char high = answer[2]; 
    //return ((int)low)+(((int)answer[2])*256);
    
    int low=(int)answer[3];
    return low;
    //int j=(int)answer[2];
    //return j;

    if(answer[2]==0x00)
      return low;
    else if (answer[2]==0x01)
      return low+256;
    else if (answer[2]==0x02)
      return low+512;
    else if (answer[2]==0x03)
      return low+768;
    else
      return 0;

    //return i+(((int)answer[2])*256);
    //return j;
    //+(((int)answer[2])*256);
    //return ((int)answer[2])*256;
    //return 1023;
  }
  else return -1;
}

int octopus_adc_ref(struct octopus_context *octopus, int ref)
{
  char answer[3];
  char msg[] = {CMD_ADC_REF,0x01,0x00};
  msg[2] = (char)ref;
  if(octopus_message(octopus,msg,3,answer,3)>0)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_adc_get_bulk(struct octopus_context *octopus, 
  int pin, int number, int * buf)
{
  return 1;
}

int octopus_i2c_init(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_I2C_INIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}

int octopus_i2c_deinit(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_I2C_DEINIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}


int octopus_i2c_set_bitrate(struct octopus_context *octopus, int speed)
{
  char answer[2];
  char msg[] = {CMD_I2C_BITRATE,0x01,0x00};
  msg[2] = (char)speed;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}


int octopus_i2c_send_bytes(struct octopus_context *octopus, 
  unsigned char *buf, int len, int timeout)
{
  if(len>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_I2C_SEND_BYTES,0x01,0x00};
  msg[1] = (char)(len+4);
  msg[2] = (char)timeout;
  //msg[3] = (char)address;

  // build send array

  int i;
  for(i=0;i<len;i++)
    msg[i+4]=(char)buf[i];

  if(octopus_message(octopus,msg,len+4,answer,2)>0)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}


int octopus_i2c_send_byte(struct octopus_context *octopus, 
  unsigned char buf,int timeout)
{
  return octopus_i2c_send_bytes(octopus,&buf,1,1); // 1ms
}


unsigned char octopus_i2c_receive_byte(struct octopus_context *octopus, 
  int address,int timeout)
{
  char tmp[1];

  if(octopus_i2c_receive_bytes(octopus,address,tmp,1,1)>0)
    return tmp[1];
  else
    return -1;
}


int octopus_i2c_receive_bytes(struct octopus_context *octopus, 
  int address,unsigned char *buf, int len, int timeout)
{
  if(len>60){
    return -1; // max 60 bytes
  }
 
  char msg[64] = {CMD_I2C_RECV_BYTES,0x01,0x00};
  msg[1] = (char)(len+4);
  msg[2] = (char)timeout;
  msg[3] = (char)address;

  char answer[64];
  int i;

  if(octopus_message(octopus,msg,4,answer,len+3)>0){
    for(i=0;i<len;i++)
      buf[i]=answer[i+3];
    return (int)answer[2];
  }
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}

int octopus_i2c_send_start(struct octopus_context *octopus)
{
  char answer[3];
  char msg[] = {CMD_I2C_SEND_START,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

  return 1;
}

int octopus_i2c_send_stop(struct octopus_context *octopus)
{
  char answer[3];
  char msg[] = {CMD_I2C_SEND_STOP,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

  return 1;
}

/// part: SPI
int octopus_spi_init(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_SPI_INIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

}

int octopus_spi_deinit(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_SPI_DEINIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

}

int octopus_spi_speed(struct octopus_context *octopus, int speed)
{

}


int octopus_spi_send(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_SPI_SEND,0x01,0x00};
  msg[1] = (char)(length);

  // build send array
  int i;
  for(i=0;i<length;i++)
    msg[i+2]=(char)buf[i];

  if(octopus_message(octopus,msg,2+length,answer,2)>0)
    return 1; 
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}

int octopus_spi_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[64];

  char msg[64] = {CMD_SPI_RECV,0x01,0x00};
  msg[1] = (char)(length);
  
  int i;

  if(octopus_message(octopus,msg,2,answer,2+length)>0) {
    for(i=0;i<length;i++)
      buf[i]=answer[i+2];
   
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);
}

int octopus_spi_send_and_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_SPI_SEND_AND_RECV,0x01,0x00};
  msg[1] = (char)(length);

  // build send array
  int i;
  for(i=0;i<length;i++)
    msg[i+2]=(char)buf[i];

  if(octopus_message(octopus,msg,2+length,answer,2+length)>0) {
    
    for(i=0;i<length;i++)
      buf[i]=answer[i+2];
 
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;

}




/// part: UART
int octopus_uart_init(struct octopus_context *octopus, int uartport){}
int octopus_uart_deinit(struct octopus_context *octopus, int uartport){}

int octopus_uart_default(struct octopus_context *octopus, int uartport){}
int octopus_uart_speed(struct octopus_context *octopus, int uartport, int speed){}
int octopus_uart_stopbits(struct octopus_context *octopus, int uartport, int stopbits){}
int octopus_uart_databits(struct octopus_context *octopus, int uartport, int databits){}
int octopus_uart_parity(struct octopus_context *octopus, int uartport, int parity){}

int octopus_uart_send(struct octopus_context *octopus, int uartport, unsigned char * buf, int length){}
int octopus_uart_recv(struct octopus_context *octopus, int uartport, unsigned char * buf, int length){}


